package cyy.example.com.facotry.net.helper;

import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;

import com.example.com.common.utils.AppUtils;
import com.example.com.common.utils.L;

import org.greenrobot.greendao.Property;
import org.greenrobot.greendao.query.QueryBuilder;

import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import cyy.example.com.facotry.Factory;
import cyy.example.com.facotry.model.db.BaseDbModel;
import cyy.example.com.facotry.model.db.dao.DaoMaster;
import cyy.example.com.facotry.model.db.dao.DaoSession;
import cyy.example.com.facotry.share.SPEngine;

/**
 * Created by ChenYiYao on 2018/11/28.
 * des : 数据库辅助类
 */
public class DbHelper {
    private static DbHelper instance;
    private static String mDbName = "info-evn-db";
    private static int NOTIFY_SAVE = 1001; //增改通知
    private static int NOTIFY_QUERRY = 1002; // 查询类通知
    private static int NOTIFY_DELETE = 1003; //删除类通知


    /**
     * 观察者集合
     * class 观察的表
     * Set<ChangedListener> 每个表对应的多个观察者
     */
    private final Map<Class<?>, Set<ChangedListener>> changedListeners = new HashMap<>();

    static {
        instance = new DbHelper();
    }


    private DaoSession mDaoSession;
    private DaoMaster.DevOpenHelper mDevOpenHelper;


    public synchronized static DbHelper getInstance() {
        return instance;
    }


    public DbHelper() {
        setDebug(AppUtils.isAppDebug());
    }

    /**
     * 获取某个表的监听者
     *
     * @param modelClass 表
     * @param <T>
     * @return
     */
    private <T extends BaseDbModel<T>> Set<ChangedListener> getListeners(Class<T> modelClass) {
        if (changedListeners.containsKey(modelClass)) {
            return changedListeners.get(modelClass);
        }
        return null;
    }

    /**
     * 添加一个监听
     *
     * @param tClass   对某个表关注
     * @param listener 监听者
     * @param <Model>  表的范型
     */
    public static <Model extends BaseDbModel> void addChangedListener(final Class<Model> tClass,
                                                                      ChangedListener<Model> listener) {
        Set<ChangedListener> changedListeners = instance.getListeners(tClass);
        if (changedListeners == null) {
            // 初始化某一类型的容器
            changedListeners = new HashSet<>();
            // 添加到中的Map
            instance.changedListeners.put(tClass, changedListeners);
        }
        changedListeners.add(listener);
    }


    /**
     * 删除某一个表的某一个监听器
     *
     * @param tClass   表
     * @param listener 监听器
     * @param <Model>  表的范型
     */
    public static <Model extends BaseDbModel> void removeChangedListener(final Class<Model> tClass,
                                                                         ChangedListener<Model> listener) {
        Set<ChangedListener> changedListeners = instance.getListeners(tClass);
        if (changedListeners == null) {
            // 容器本身为null，代表根本就没有
            return;
        }
        // 从容器中删除你这个监听者
        changedListeners.remove(listener);
    }


    /**
     * 初始化数据相关参数
     */
    public synchronized void initDB() {
        closeDaoSeesion();
        closeDaoHelper();
        String user_id = SPEngine.getSPEngine().getUserInfo().getUser_id();
        mDevOpenHelper = new DaoMaster.DevOpenHelper(Factory.app(), user_id + mDbName, null);
        SQLiteDatabase db = mDevOpenHelper.getWritableDatabase();
        DaoMaster daoMaster = new DaoMaster(db);
        mDaoSession = daoMaster.newSession();
    }

    /**
     * 获取DaoSession
     *
     * @return
     */
    public synchronized DaoSession getDaoSession() {
        if (null == mDaoSession) {
            SQLiteDatabase db = getDevOpenHelper().getWritableDatabase();
            DaoMaster daoMaster = new DaoMaster(db);
            mDaoSession = daoMaster.newSession();
        }
        return mDaoSession;
    }

    /**
     * 获取数据库操作类
     *
     * @return
     */
    private DaoMaster.DevOpenHelper getDevOpenHelper() {
        if (mDevOpenHelper == null) {
            String user_id = SPEngine.getSPEngine().getUserInfo().getUser_id();
            mDevOpenHelper = new DaoMaster.DevOpenHelper(Factory.app(), user_id + mDbName, null);
        }
        return mDevOpenHelper;
    }

    public synchronized void closeDataBase() {
        closeDaoSeesion();
        closeDaoHelper();
    }

    private void closeDaoSeesion() {
        if (mDaoSession != null) {
            mDaoSession.clear();
            mDaoSession = null;
        }
    }

    private void closeDaoHelper() {
        if (mDevOpenHelper != null) {
            mDevOpenHelper.close();
            mDevOpenHelper = null;
        }
    }


    /**
     * 设置debug模式开启或关闭，默认关闭
     *
     * @param flag
     */
    public void setDebug(boolean flag) {
        QueryBuilder.LOG_SQL = flag;
        QueryBuilder.LOG_VALUES = flag;
    }


    /***************************  数据库插入或更新操作  ******************************/

    /**
     * @param tClass  插入类型
     * @param obj     数据实体
     * @param <Model> 泛型
     * @return
     */
    public static <Model extends BaseDbModel<Model>> boolean saveObject(final Class<Model> tClass,
                                                                        Model obj) {
        boolean flag = false;
        try {
            flag = instance.getDaoSession().insertOrReplace(obj) != -1 ? true : false;
            /**
             * 此处若将obj直接传入notifySave，在观察者的子类若重写通知方法则会抛类型转换异常
             */
            Model[] models = instance.create(tClass, 1);
            models[0] = obj;
            instance.notify(NOTIFY_SAVE, tClass, models);
        } catch (Exception e) {
            L.e(e.toString());
        }
        return flag;
    }


    /**
     * 插入多个对象，开启线程插入
     *
     * @param tClass  表类型
     * @param obj     对象
     * @param <Model> 泛型
     * @return
     */
    public static <Model extends BaseDbModel<Model>> boolean saveMultObject(final Class<Model> tClass, final List<Model> obj) {
        boolean flag = false;
        if (obj == null || obj.isEmpty()) {
            return flag;
        }

        try {
            instance.getDaoSession().runInTx(new Runnable() {
                @Override
                public void run() {
                    for (Model data : obj) {
                        instance.getDaoSession().insertOrReplace(data);
                        instance.notify(NOTIFY_SAVE, tClass, obj.toArray(instance.create(tClass, obj.size())));
                    }
                }
            });
            flag = true;
        } catch (Exception e) {
            L.e(e.toString());
            flag = false;
        }
        return flag;
    }


    /***************************  数据库删除操作  ******************************/


    /**
     * 删除数据库某个表
     *
     * @param cl 表
     * @return
     */
    public static <Model extends BaseDbModel<Model>> boolean deleteAll(Class<Model> cl) {
        boolean flag = false;

        try {
            instance.getDaoSession().deleteAll(cl);
            flag = true;
            Set<ChangedListener> listeners = instance.getListeners(cl);
            if (listeners != null && listeners.size() > 0) {
                //通知
                for (ChangedListener<Model> listener : listeners) {
                    listener.onTableDelete();
                }
            }
        } catch (Exception e) {
            L.e(e.toString());
        }
        return flag;
    }

    /**
     * 删除单行数据
     *
     * @return
     */
    public static <Model extends BaseDbModel<Model>> boolean deleteObject(final Class<Model> tClass, final Model obj) {
        boolean flag = false;
        try {
            instance.getDaoSession().delete(obj);
            /**
             * 此处若将obj直接传入notifySave，观察者的子类若重写通知方法则会抛类型转换异常
             */
            Model[] models = instance.create(tClass, 1);
            models[0] = obj;
            instance.notify(NOTIFY_DELETE, tClass, models);
            flag = true;
        } catch (Exception e) {
            L.e(e.toString());
        }
        return flag;
    }


    /**
     * 删除多个对象，开启线程
     *
     * @param obj
     * @param <T>
     * @return
     */
    public static <T extends BaseDbModel<T>> boolean deleteMultObject(final Class<T> tClass, final List<T> obj) {
        boolean flag = false;

        if (obj == null || obj.isEmpty()) {
            return flag;
        }

        try {
            instance.getDaoSession().runInTx(new Runnable() {
                @Override
                public void run() {
                    for (T data : obj) {
                        instance.getDaoSession().delete(data);
                    }
                    instance.notify(NOTIFY_DELETE, tClass, obj.toArray(instance.create(tClass, obj.size())));
                }
            });
            flag = true;
        } catch (Exception e) {
            L.e(e.toString());
            flag = false;
        }
        return flag;
    }


    /***************************  数据库查询操作  ******************************/

    /**
     * 获取表明
     */
    public static String getTableName(Class cl) {
        return instance.getDaoSession().getDao(cl).getTablename();
    }

    /**
     * 获取所有的数据
     *
     * @param cl      表类型
     * @param <Model>
     * @return
     */
    public static <Model extends BaseDbModel<Model>> List<Model> QueryAll(Class<Model> cl) {
        List<Model> datas = null;

        try {
            datas = (List<Model>) instance.getDaoSession().getDao(cl).loadAll();
            if (datas != null && datas.size() > 0)
                instance.notify(NOTIFY_QUERRY, cl, datas.toArray(instance.create(cl, datas.size())));

        } catch (Exception e) {
            L.w(e.toString());
        }
        return datas;
    }

    /**
     * 降序查询所有数据
     *
     * @param cl
     * @param properties
     * @param <Model>
     * @return
     */
    public static <Model extends BaseDbModel<Model>> List<Model> QueryAllOrderDesc(Class<Model> cl, Property... properties) {
        List<Model> datas = null;
        try {
            datas = instance.getDaoSession().queryBuilder(cl).orderAsc(properties).list();
            if (datas != null && datas.size() > 0)
                instance.notify(NOTIFY_QUERRY, cl, datas.toArray(instance.create(cl, datas.size())));

        } catch (Exception e) {
            L.w(e.toString());
        }
        return datas;
    }

    /**
     * 升序查询所有数据
     *
     * @param cl
     * @param properties
     * @param <Model>
     * @return
     */
    public static <Model extends BaseDbModel<Model>> List<Model> QueryAllOrderAsc(Class<Model> cl, Property... properties) {
        List<Model> datas = null;

        try {
            datas = (List<Model>) instance.getDaoSession().queryBuilder(cl).orderAsc(properties);
            if (datas != null && datas.size() > 0)
                instance.notify(NOTIFY_QUERRY, cl, datas.toArray(instance.create(cl, datas.size())));

        } catch (Exception e) {
            L.w(e.toString());
        }
        return datas;
    }


    /**
     * 数据更新通知
     *
     * @param notifyType 通知类型：   NOTIFY_SAVE,  NOTIFY_DELETE ,NOTIFY_QUERRY
     * @param tClass
     * @param models
     * @param <Model>
     */
    private final <Model extends BaseDbModel<Model>> void notify(final int notifyType, final Class<Model> tClass,
                                                                 final Model... models) {
        /**
         * 通知发送切回主线程
         */
        new Handler(Factory.app().getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                if (notifyType == NOTIFY_SAVE) {
                    notifySave(tClass, models);
                } else if (notifyType == NOTIFY_QUERRY) {
                    notifyQuery(tClass, models);
                } else if (notifyType == NOTIFY_DELETE) {
                    notifyDelete(tClass, models);
                }
            }
        });

    }

    /**
     * 数据更新通知
     *
     * @param tClass  消息类型
     * @param models  通知的modle数据
     * @param <Model> 实例的泛型
     */
    private final <Model extends BaseDbModel<Model>> void notifySave(final Class<Model> tClass,
                                                                     final Model... models) {
        Set<ChangedListener> listeners = getListeners(tClass);
        if (listeners != null && listeners.size() > 0) {
            //通用的通知
            for (ChangedListener<Model> listener : listeners) {
                listener.onDataSave(models);
            }
        }
    }


    /**
     * 消息删除通知
     *
     * @param tClass  消息类型
     * @param models  通知的modle数据
     * @param <Model> 实例的泛型
     */
    private final <Model extends BaseDbModel<Model>> void notifyDelete(final Class<Model> tClass,
                                                                       final Model... models) {
        Set<ChangedListener> listeners = getListeners(tClass);
        if (listeners != null && listeners.size() > 0) {
            //通知
            for (ChangedListener<Model> listener : listeners) {
                listener.onDataDelete(models);
            }
        }
    }

    /**
     * 查询通知
     *
     * @param tClass
     * @param models
     * @param <Model>
     */
    private final <Model extends BaseDbModel<Model>> void notifyQuery(final Class<Model> tClass,
                                                                      final Model... models) {
        Set<ChangedListener> listeners = getListeners(tClass);
        if (listeners != null && listeners.size() > 0) {
            //通知
            for (ChangedListener<Model> listener : listeners) {
                listener.onDataQuery(models);
            }
        }
    }

    private <Model extends BaseDbModel<Model>> Model[] create(Class<Model> type, int size) {
        return (Model[]) Array.newInstance(type, size);
    }


    /**
     * 通知监听器
     */
    @SuppressWarnings({"unused", "unchecked"})
    public interface ChangedListener<Data extends BaseDbModel> {
        void onDataSave(Data... list);

        void onDataQuery(Data... list);

        void onDataDelete(Data... list);

        void onTableDelete();
    }

}
